package com.aleyn.router.plug.visitor

import com.aleyn.router.plug.data.HandleModel
import org.objectweb.asm.Label
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.commons.InstructionAdapter


/**
 * @author: Aleyn
 * @date: 2023/7/10 9:50
 *
 * ASM MethodVisitor, 用于向 `LRouterGenerateImpl.injectAutowired` 方法中注入字节码。
 *
 * 这个类的核心作用是实现 `@Autowired` 注解的自动注入逻辑。
 * 它遍历所有被 `@Autowired` 注解标记的类，并为每个类生成一个静态方法调用，
 * 这个调用会指向该类中由 KSP（或 APT）生成的 `autowiredInject` 静态方法。
 *
 * 例如，如果 `com.example.TestActivity` 中有 `@Autowired` 字段，
 * 那么 KSP 会生成一个 `com.example.TestActivity$$AutowiredInjector` 类（或者类似名称），
 * 其中包含一个静态方法 `public static void autowiredInject(Object target)`。
 *
 * 此 Adapter 会在 `LRouterGenerateImpl.injectAutowired(Object obj)` 方法中生成如下等效代码：
 * ```java
 * try {
 *     com.example.TestActivity$$AutowiredInjector.autowiredInject(obj);
 * } catch (Exception e) {
 *     // ignore
 * }
 * // ... 对其他需要注入的类重复此操作
 * ```
 *
 * @param api ASM API的版本，例如 `Opcodes.ASM9`。
 * @param methodVisitor 委托的下一个 MethodVisitor。
 * @param autowiredClass 包含所有需要进行自动注入的类信息的列表。
 */
class AutowiredInstructAdapter(
    api: Int,
    methodVisitor: MethodVisitor,
    private val autowiredClass: List<HandleModel.Autowired>?
) : InstructionAdapter(api, methodVisitor) {

    /**
     * 在目标方法的代码开始处被调用。
     *
     * 这里是实现字节码注入的关键。
     * 它会遍历 `autowiredClass` 列表，为每个类生成调用其 `autowiredInject` 静态方法的指令。
     * 使用 `try-catch` 块包裹每次调用，以防止某个类的注入失败影响整个应用的运行。
     */
    override fun visitCode() {
        autowiredClass?.forEach {
            val label0 = Label()
            val label1 = Label()
            val label2 = Label()

            // 生成 try-catch(Exception) 块
            visitTryCatchBlock(label0, label1, label2, "java/lang/Exception")
            mark(label0)
            nop()
            // 加载方法的第一个参数 (Object target) 到操作数栈
            load(1, OBJECT_TYPE)
            // 调用静态方法：it.className.autowiredInject(target)
            invokestatic(
                it.className,
                "autowiredInject",
                "(Ljava/lang/Object;)V",
                false
            )
            mark(label1)
            val label3 = Label()
            goTo(label3)
            mark(label2)
            // catch 块，这里只是简单地将异常存储起来，不做任何处理
            store(2, OBJECT_TYPE)
            mark(label3)
        }
        super.visitCode()
    }

}